﻿// This file is part of Code4Public (http://code4public.codeplex.com)
// Copyright 2011 Sina Iravanian - <sina@sinairv.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
// ------------------------------------------------------------------
// The Win API techniques used in this class are taken from 
// the source code of TinyPG: a Tiny Parser Generator available at
// http://www.codeproject.com/KB/recipes/TinyPG.aspx
// by Herre Kuijpers. Here comes the copyright notice:
//------------------------------------------------------------------------
// Copyright 2008 - 2010 Herre Kuijpers - <herre.kuijpers@gmail.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Code4Public.GUI
{
    public class TextBoxUtils
    {
        #region Pubilic Methods
        public static void DisableAndDoAndEnable(TextBoxBase textBox, Action action)
        {
            IntPtr stateLocked = IntPtr.Zero;

            Lock(textBox, ref stateLocked);

            int hscroll = GetHScrollPos(textBox);
            int vscroll = GetVScrollPos(textBox);

            string text = textBox.Text;
            int selstart = textBox.SelectionStart;

            action();

            textBox.Select(selstart, 0);

            SetHScrollPos(textBox, hscroll);
            SetVScrollPos(textBox, vscroll);

            Unlock(textBox, ref stateLocked);
        }

        public static int GetHScrollPos(TextBoxBase textBox)
        {
            return GetScrollPos((int)textBox.Handle, SB_HORZ);
        }

        public static void SetHScrollPos(TextBoxBase textBox, int value)
        {
            SetScrollPos((IntPtr)textBox.Handle, SB_HORZ, value, true);
            PostMessageA((IntPtr)textBox.Handle, WM_HSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
        }

        public static int GetVScrollPos(TextBoxBase textBox)
        {
            return GetScrollPos((int)textBox.Handle, SB_VERT);
        }

        public static void SetVScrollPos(TextBoxBase textBox, int value)
        {
            SetScrollPos((IntPtr)textBox.Handle, SB_VERT, value, true);
            PostMessageA((IntPtr)textBox.Handle, WM_VSCROLL, SB_THUMBPOSITION + 0x10000 * value, 0);
        }
        #endregion

        #region Private Methods
        private static void Lock(TextBoxBase textBox, ref IntPtr stateLocked)
        {
            // Stop redrawing:  
            SendMessage(textBox.Handle, WM_SETREDRAW, 0, IntPtr.Zero);
            // Stop sending of events:  
            stateLocked = SendMessage(textBox.Handle, EM_GETEVENTMASK, 0, IntPtr.Zero);
            // change colors and stuff in the RichTextBox  
        }

        private static void Unlock(TextBoxBase textBox, ref IntPtr stateLocked)
        {
            // turn on events  
            SendMessage(textBox.Handle, EM_SETEVENTMASK, 0, stateLocked);
            // turn on redrawing  
            SendMessage(textBox.Handle, WM_SETREDRAW, 1, IntPtr.Zero);

            stateLocked = IntPtr.Zero;
            textBox.Invalidate();
        }
        #endregion

        #region Win API Stuff

        // Windows APIs
        [DllImport("user32", CharSet = CharSet.Auto)]
        private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool PostMessageA(IntPtr hWnd, int nBar, int wParam, int lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int GetScrollPos(int hWnd, int nBar);

        [DllImport("user32.dll")]
        private static extern int SetScrollPos(IntPtr hWnd, int nBar, int nPos, bool bRedraw);

        private const int WM_SETREDRAW = 0x000B;
        private const int WM_USER = 0x400;
        private const int EM_GETEVENTMASK = (WM_USER + 59);
        private const int EM_SETEVENTMASK = (WM_USER + 69);
        private const int SB_HORZ = 0x0;
        private const int SB_VERT = 0x1;
        private const int WM_HSCROLL = 0x114;
        private const int WM_VSCROLL = 0x115;
        private const int SB_THUMBPOSITION = 4;
        private const int UNDO_BUFFER = 100;

        #endregion
    }
}
